import gradio as gr
from login import AuthenticatedApp  # 假设这个类存在且工作正常
from openai_tools import generate_illustration  # 假设这个函数存在
from prompt_templates import PromptTemplates, prompt_default  # 假设这些存在
from tools import logger  # 假设这个存在
from credits import CreditsManager  # 导入点数管理模块


# --- Mocked CreditsManager for demonstration ---
# In a real scenario, CreditsManager would interact with a database
# and a payment gateway SDK (like alipay-sdk-python, etc.)
class MockedCreditsManager(CreditsManager):
    def __init__(self, db_manager):
        super().__init__(db_manager)
        # Simulate a database for orders
        # self.orders = {} # {out_trade_no: {"username": "user", "amount": 10, "status": "PENDING"}}

    def create_payment_url(self, username, amount, pay_type_value, host_url):
        # ... (original logic for generating out_trade_no)
        out_trade_no = super()._generate_out_trade_no(username)  # Assuming this exists

        # Store order info (username is crucial)
        # In a real DB: INSERT INTO orders (out_trade_no, username, amount_ yuan, status) VALUES (?, ?, ?, 'PENDING')
        self.db_manager.execute_query(
            "INSERT OR REPLACE INTO payment_orders (out_trade_no, username, amount, currency, credits_to_add, status, payment_method) VALUES (?, ?, ?, ?, ?, ?, ?)",
            (
                out_trade_no,
                username,
                float(amount),
                "CNY",
                int(float(amount) * 10),
                "PENDING",
                pay_type_value,
            ),
        )
        logger.info(
            f"Order created: {out_trade_no} for user {username}, amount {amount}"
        )

        # Simulate payment gateway redirect URL
        # The payment gateway will append its own parameters like trade_no, sign, etc.
        # to this return_url when redirecting.
        # For EasyPay, it might be something like:
        # http://localhost:7890/?trade_no=2021ABC&out_trade_no=XYZ123&type=alipay&money=0.01&name=...&sign=...&sign_type=MD5&trade_status=TRADE_SUCCESS
        return_url = f"{host_url}/?custom_payment_return=true"  # Add a marker

        # This would be the actual payment gateway URL
        # payment_gateway_url = f"https://payment.provider.com/pay?order_id={out_trade_no}&amount={amount}&return_url={return_url}"
        # For the sake of the example, we'll use a simplified mock redirect from EasyPay example
        # In real EasyPay, you'd use their SDK to build the request.
        # This is just a placeholder for the payment provider's URL.
        # The actual URL will come from your payment provider's SDK.
        # For EasyPay, it typically redirects to a page on their domain.
        # After payment, EasyPay redirects to YOUR return_url.

        # For local testing, we can simulate the redirect by constructing a URL that looks like
        # what the payment provider would send back to our return_url.
        # Example: http://localhost:7890/?out_trade_no=OUR_ORDER_ID&trade_no=GATEWAY_TID&money=0.01&type=alipay&trade_status=TRADE_SUCCESS
        # This is what our `demo.load` will parse.
        # The iframe will actually load the *payment provider's* page.
        # The key is that THE PAYMENT PROVIDER will redirect the BROWSER (not just the iframe) to your return_url.

        # The payment_url should be the one provided by your payment gateway SDK
        # For EasyPay or similar, it might be a direct URL to their service or an HTML form to submit.
        # Let's assume easy_pay_client.submit(...) returns this URL
        # For this example, we'll construct a *mock* payment page that then redirects back with params
        mock_payment_provider_page_url = f"{host_url}/mock_pay_page?out_trade_no={out_trade_no}&amount={amount}&pay_type={pay_type_value}&actual_return_url={host_url}/"

        logger.info(f"Mock payment URL for iframe: {mock_payment_provider_page_url}")

        payment_url_for_iframe = f"https://some-payment-gateway.com/pay?orderId={out_trade_no}&amount={amount}&returnUrl={host_url}/"
        # For testing, let's make a local mock page that simulates the payment and redirect
        payment_url_for_iframe = f"{host_url}/_internal_mock_payment_processor_?out_trade_no={out_trade_no}&username={username}&amount={amount}"

        return payment_url_for_iframe, out_trade_no

    def handle_successful_payment_notification(
        self,
        out_trade_no: str,
        gateway_trade_no: str,
        paid_amount_str: str,
        username_from_order: str,
    ):
        """
        This function would be called ideally by a webhook (notify_url) or
        as part of return_url processing if the gateway confirms payment.
        It updates the order status and credits.
        Returns: dict with success, username, new_credits, message
        """
        logger.info(
            f"Processing successful payment for order {out_trade_no}, gateway ref: {gateway_trade_no}"
        )

        # 1. Fetch order details from DB using out_trade_no
        order_details_row = self.db_manager.fetch_one(
            "SELECT username, amount, credits_to_add, status FROM payment_orders WHERE out_trade_no = ?",
            (out_trade_no,),
        )

        if not order_details_row:
            logger.error(f"Order {out_trade_no} not found in DB.")
            return {"success": False, "message": "订单未找到"}

        db_username, db_amount, db_credits_to_add, db_status = order_details_row

        # Security: ensure username matches if passed, or trust username from DB
        if (
            db_username != username_from_order
        ):  # username_from_order is the one who initiated the payment UI
            logger.warning(
                f"Username mismatch for order {out_trade_no}. DB: {db_username}, Initiator: {username_from_order}. Using DB username."
            )
            # This can happen if admin pays for user, or if something is fishy. Prioritize DB record.

        if db_status == "COMPLETED":
            logger.info(
                f"Order {out_trade_no} already processed for user {db_username}."
            )
            current_credits = self.get_user_credits(db_username)
            return {
                "success": True,
                "username": db_username,
                "new_credits": current_credits,
                "message": "订单已处理，点数已增加",
            }

        # 2. Validate amount (optional but good practice)
        #    paid_amount = decimal.Decimal(paid_amount_str)
        #    if paid_amount != decimal.Decimal(str(db_amount)):
        #        logger.error(f"Amount mismatch for order {out_trade_no}. Expected {db_amount}, got {paid_amount}")
        #        return {"success": False, "message": "支付金额不匹配"}

        # 3. Add credits to user
        try:
            self.add_credits(
                db_username, db_credits_to_add, f"充值 - 订单 {out_trade_no}"
            )
            new_total_credits = self.get_user_credits(db_username)
            logger.info(
                f"User {db_username} credited with {db_credits_to_add}. New balance: {new_total_credits}"
            )

            # 4. Update order status to COMPLETED
            self.db_manager.execute_query(
                "UPDATE payment_orders SET status = ?, gateway_trade_no = ? WHERE out_trade_no = ?",
                ("COMPLETED", gateway_trade_no, out_trade_no),
            )
            logger.info(f"Order {out_trade_no} marked as COMPLETED.")

            return {
                "success": True,
                "username": db_username,
                "new_credits": new_total_credits,
                "message": f"充值成功！增加 {db_credits_to_add} 点数。",
            }
        except Exception as e:
            logger.exception(
                f"Error processing payment for order {out_trade_no} for user {db_username}: {e}"
            )
            return {"success": False, "message": f"处理支付时发生内部错误: {e}"}


# --- End Mocked CreditsManager ---


def create_gradio_interface():
    app = AuthenticatedApp()
    # Use the mocked one for now, or your actual one if it's ready
    # credits_manager = CreditsManager(app.db_manager)
    credits_manager = MockedCreditsManager(app.db_manager)

    with gr.Blocks(theme=gr.themes.Soft()) as demo:  # Added a theme for better looks
        # These states will be reset on full page reload.
        # Their values will be repopulated by demo.load() if a payment return is detected.
        session_id = gr.State("")
        current_template = gr.State("city_scene")
        current_username = gr.State("")

        with gr.Tab("账户"):
            with gr.Row():
                with gr.Column(scale=1):
                    with gr.Group():
                        username_input = gr.Textbox(
                            label="用户名", value="user1"
                        )  # Default for testing
                        password_input = gr.Textbox(
                            label="密码", type="password", value="pass1"
                        )  # Default for testing
                        register_btn = gr.Button("注册")
                        login_btn = gr.Button("登录")
                        logout_btn = gr.Button("登出")
                        account_status = gr.Textbox(label="状态", interactive=False)
                        user_credits = gr.Textbox(
                            label="我的点数", value="未登录", interactive=False
                        )

                with gr.Column(scale=2):  # Made recharge column wider
                    with gr.Group():
                        gr.Markdown("### 点数充值")
                        gr.Markdown("1元 = 10点数，生成1张图片消耗5点数")
                        recharge_amount = gr.Number(
                            label="充值金额(元)",
                            value=0.01,
                            minimum=0.01,
                            precision=2,
                            step=0.01,
                        )
                        pay_type = gr.Dropdown(
                            ["支付宝(alipay)", "微信支付(wxpay)", "QQ钱包(qqpay)"],
                            label="支付方式",
                            value="支付宝(alipay)",
                        )
                        recharge_btn = gr.Button("充值点数", variant="primary")
                        payment_result = gr.Textbox(label="充值结果", interactive=False)
                        payment_frame = gr.HTML(
                            visible=False
                        )  # Iframe to load payment page

            def handle_register(username, password):
                result = app.register(username, password)
                if result["success"]:
                    # Automatically log in after registration
                    login_result = app.login(username, password)
                    if login_result["success"]:
                        current_credits = credits_manager.get_user_credits(username)
                        return (
                            login_result["message"],
                            login_result["session_id"],
                            username,
                            f"{current_credits} 点",
                        )
                    return (
                        "注册成功，但自动登录失败: " + login_result["message"],
                        "",
                        "",
                        "未登录",
                    )
                return result["message"], "", "", "未登录"

            def handle_login(username, password):
                result = app.login(username, password)
                if result["success"]:
                    current_credits = credits_manager.get_user_credits(username)
                    return (
                        result["message"],
                        result["session_id"],
                        username,
                        f"{current_credits} 点",
                    )
                return result["message"], "", "", "未登录"

            def handle_logout(curr_session_id):
                app.logout(curr_session_id)  # logout doesn't usually return much
                return (
                    "已登出",
                    "",
                    "",
                    "未登录",
                )  # Update status, clear username, session, credits

            def handle_recharge(amount, payment_method, curr_username_val):
                if not curr_username_val:  # Check value of state
                    return "请先登录后再充值", gr.update(visible=False)

                if amount <= 0:
                    return "充值金额必须大于0", gr.update(visible=False)

                pay_type_value = payment_method.split("(")[1].rstrip(")")

                # IMPORTANT: Use the correct public URL if deploying. For local, localhost is fine.
                # The payment gateway will redirect to this host.
                host_url = "http://localhost:7890"  # Gradio default
                # If running via `share=True`, Gradio gives a tunnel URL. That should be used.
                # Or if behind Nginx/Cloudflare, use the public domain.

                try:
                    payment_url_for_iframe, out_trade_no = (
                        credits_manager.create_payment_url(
                            curr_username_val, amount, pay_type_value, host_url
                        )
                    )
                    logger.info(
                        f"User {curr_username_val} attempting recharge. Order: {out_trade_no}. iframe URL: {payment_url_for_iframe}"
                    )

                    # The iframe will load the payment provider's page.
                    # After payment, the provider redirects the *entire browser window* to your return_url.
                    # The `onload` here is for the iframe itself, not the final payment success.
                    iframe_html = f'<iframe src="{payment_url_for_iframe}" style="width:100%;height:400px;border:1px solid #ccc;" sandbox="allow-scripts allow-same-origin allow-forms allow-popups"></iframe>'

                    return f"订单 {out_trade_no} 创建成功，请在下方扫码或跳转到支付页面完成支付。", gr.update(
                        visible=True, value=iframe_html
                    )
                except Exception as e:
                    logger.exception("Error during handle_recharge:")
                    return f"创建支付链接失败: {str(e)}", gr.update(visible=False)

            register_btn.click(
                handle_register,
                inputs=[username_input, password_input],
                outputs=[account_status, session_id, current_username, user_credits],
            )

            login_btn.click(
                handle_login,
                inputs=[username_input, password_input],
                outputs=[account_status, session_id, current_username, user_credits],
            )

            logout_btn.click(
                handle_logout,
                inputs=[session_id],  # Pass current session_id
                outputs=[
                    account_status,
                    session_id,
                    current_username,
                    user_credits,
                ],  # Clear session_id and username on logout
            )

            recharge_btn.click(
                handle_recharge,
                inputs=[
                    recharge_amount,
                    pay_type,
                    current_username,
                ],  # Pass current_username state
                outputs=[payment_result, payment_frame],
            )

        # ... (Rest of your "深度记事-明信片插画生成" Tab, unchanged for now but ensure it uses `current_username.value` and `session_id.value`)
        with gr.Tab("深度记事-明信片插画生成"):
            with gr.Row():
                with gr.Column(scale=1):
                    city_scene_btn = gr.Button("城市景点介绍", variant="secondary")
                    knowledge_btn = gr.Button("知识学习小报", variant="secondary")
                    custom_template_btn = gr.Button(
                        "新增提示词词模版", variant="secondary"
                    )
                    gr.Markdown("---")
                    generate_prompt_btn = gr.Button("生成提示词", variant="primary")
                    generate_image_btn = gr.Button("一键生图", variant="primary")

                with gr.Column(scale=1):
                    with gr.Group(visible=True) as city_scene_group:
                        gr.Markdown("### 城市景点介绍")
                        city_input = gr.Textbox(label="城市", value="杭州")
                        scene_input = gr.Textbox(label="景区", value="西湖")
                    with gr.Group(visible=False) as knowledge_group:
                        gr.Markdown("### 知识学习小报")
                        topic_input = gr.Textbox(label="主题", value="AI强化学习")
                    with gr.Group(visible=False) as custom_template_group:
                        gr.Markdown("### 自定义提示词")
                        custom_prompt = gr.Textbox(label="自定义提示词", lines=15)

                with gr.Column(scale=1):
                    with gr.Group() as params_group:
                        gr.Markdown("### 通用参数")
                        language = gr.Dropdown(
                            ["中文", "英文"], label="语言", value="中文"
                        )
                        ratio = gr.Dropdown(
                            ["1*1", "3*2", "2*3"], label="比例", value="1*1"
                        )
                        num_images = gr.Dropdown(
                            ["1", "2", "4"], label="生图数量", value="1"
                        )  # Default 1 for cost
                        other_attrs = gr.Textbox(
                            label="其他属性", value="真实风格，字体置灰"
                        )

                with gr.Column(scale=1):
                    prompt_output = gr.Textbox(
                        label="生成的提示词/输出结果",
                        lines=15,
                        value=prompt_default,
                        interactive=True,
                    )  # Make it interactive
            image_output = gr.Gallery(
                label="生成的图像", columns=2, height="auto"
            )  # height auto

            def show_city_scene_ui():
                return (
                    gr.update(visible=True),
                    gr.update(visible=False),
                    gr.update(visible=False),
                    "city_scene",
                )

            def show_knowledge_ui():
                return (
                    gr.update(visible=False),
                    gr.update(visible=True),
                    gr.update(visible=False),
                    "knowledge",
                )

            def show_custom_template_ui():
                return (
                    gr.update(visible=False),
                    gr.update(visible=False),
                    gr.update(visible=True),
                    "custom",
                )

            city_scene_btn.click(
                show_city_scene_ui,
                outputs=[
                    city_scene_group,
                    knowledge_group,
                    custom_template_group,
                    current_template,
                ],
            )
            knowledge_btn.click(
                show_knowledge_ui,
                outputs=[
                    city_scene_group,
                    knowledge_group,
                    custom_template_group,
                    current_template,
                ],
            )
            custom_template_btn.click(
                show_custom_template_ui,
                outputs=[
                    city_scene_group,
                    knowledge_group,
                    custom_template_group,
                    current_template,
                ],
            )

            def create_prompt_text(
                city, scene, topic, custom, lang, rat, n_img, attrs, template_type_val
            ):
                if template_type_val == "city_scene":
                    return PromptTemplates.city_scene_template(
                        city, scene, lang, rat, n_img, attrs
                    )
                elif template_type_val == "knowledge":
                    return PromptTemplates.knowledge_template(
                        topic, lang, rat, n_img, attrs
                    )
                elif template_type_val == "custom":
                    return custom
                return "请先选择一个模板"

            generate_prompt_btn.click(
                create_prompt_text,
                inputs=[
                    city_input,
                    scene_input,
                    topic_input,
                    custom_prompt,
                    language,
                    ratio,
                    num_images,
                    other_attrs,
                    current_template,
                ],
                outputs=[prompt_output],
            )

            def generate_images_ui(
                prompt_text,
                num_img_str,
                ratio_val,
                current_session_id_val,
                current_username_val,
            ):
                logger.info(
                    f"Image gen request by user: {current_username_val} (session: {current_session_id_val}), prompt: {prompt_text[:50]}..."
                )

                if not current_username_val or not app.require_auth(
                    current_session_id_val
                ):  # Check auth
                    logger.warning("Image gen failed: User not authenticated.")
                    return (
                        "请先登录或会话已过期，请重新登录后再生成图像。",
                        None,
                        gr.update(),
                    )  # Keep prompt, no images, update credits

                username = current_username_val  # Already validated by require_auth essentially
                required_credits = int(num_img_str) * 5

                if not credits_manager.check_credits_sufficient(
                    username, required_credits
                ):
                    current_c = credits_manager.get_user_credits(username)
                    logger.warning(
                        f"Image gen failed for {username}: Insufficient credits. Has {current_c}, needs {required_credits}"
                    )
                    return (
                        f"点数不足 ({current_c})，需要 {required_credits} 点。请充值。",
                        None,
                        f"{current_c} 点",
                    )

                size_map = {"1*1": "1024x1024", "3*2": "1792x1024", "2*3": "1024x1792"}
                size = size_map.get(ratio_val, "1024x1024")

                try:
                    credits_manager.consume_credits(
                        username,
                        required_credits,
                        f"生成{num_img_str}张图片 - {ratio_val}",
                    )
                    logger.info(
                        f"Credits consumed for {username}: {required_credits}. Generating images..."
                    )
                    image_urls = generate_illustration(
                        prompt=prompt_text,
                        n=int(num_img_str),
                        size=size,
                        model="dall-e-2",
                    )  # Check model name

                    current_c = credits_manager.get_user_credits(username)
                    if not image_urls:
                        credits_manager.refund_credits(
                            username, required_credits, "生成失败退回点数"
                        )
                        current_c = credits_manager.get_user_credits(
                            username
                        )  # Fetch again after refund
                        logger.error(
                            f"Image gen API failed for {username}. Credits refunded."
                        )
                        return (
                            "图像生成失败 (API未返回图片)，点数已退回。",
                            None,
                            f"{current_c} 点",
                        )

                    logger.info(
                        f"Image gen success for {username}. Remaining credits: {current_c}"
                    )
                    return (
                        f"图像生成成功! 消耗{required_credits}点。剩余: {current_c}点",
                        image_urls,
                        f"{current_c} 点",
                    )

                except Exception as e:
                    credits_manager.refund_credits(
                        username, required_credits, f"生成错误退回点数: {str(e)}"
                    )
                    current_c = credits_manager.get_user_credits(
                        username
                    )  # Fetch again after refund
                    logger.exception(
                        f"Image gen error for {username}: {e}. Credits refunded."
                    )
                    return (
                        f"生成图像时出错: {str(e)}。点数已退回。",
                        None,
                        f"{current_c} 点",
                    )

            generate_image_btn.click(
                generate_images_ui,
                inputs=[
                    prompt_output,
                    num_images,
                    ratio,
                    session_id,
                    current_username,
                ],  # Pass states
                outputs=[
                    prompt_output,
                    image_output,
                    user_credits,
                ],  # prompt_output for messages, image_output for gallery, user_credits to update display
            )

        # THIS IS THE CRITICAL PART FOR HANDLING PAYMENT REDIRECTS
        # This function runs when the Gradio app page loads/reloads.
        # It checks URL parameters for payment gateway redirect info.
        def handle_page_load_and_payment_redirect(
            # These are the initial values of the states when the page loads
            # We need to pass current_username State here because its .value will be empty on a hard reload
            # but we might need to restore it if a payment for a user is detected.
            # gr.Request is automatically passed if type-hinted in the function signature
            # and the function is used in `demo.load(inputs=...)`
            # However, for demo.load, you list the State objects in inputs, not their values.
            # Inside the function, you access them via request object for URL params,
            # and state objects for their *current* (possibly empty) values.
            # Let's get current_username_state_val to see what Gradio thinks it is before we potentially change it
            # We list `current_username` in `inputs` for `demo.load`
            # `gr.Request` will be passed automatically if type-hinted.
            # Signature for demo.load `fn`:
            # It receives arguments based on `inputs` list of `demo.load`
            # And if `gr.Request` is type-hinted, it's passed as the first argument if not in `inputs`,
            # or at the corresponding position if it IS in `inputs`.
            # Let's assume gr.Request is passed implicitly if type-hinted.
            # For other states, we pass them in `inputs` to `demo.load`
            # username_on_page_load: str, # This would be current_username.value
            request: gr.Request,
        ):
            params = request.query_params
            logger.info(f"Page loaded. URL Params: {params}")

            # These will be the values returned to update the Gradio states/components
            # Order must match `outputs` of `demo.load`
            # current_username_val, user_credits_val, account_status_val, payment_result_val, payment_frame_update

            # Default state for a normal page load without payment params
            # If current_username_state.value has something (e.g. after login, no redirect), use it
            # This part is tricky as current_username state itself might be empty after a redirect
            # We rely on the payment system to tell us THE user.

            # Initial values to return if not a payment callback
            # We cannot reliably get `current_username.value` here as it might be stale after reload.
            # The states listed in `outputs` of `demo.load` will be updated by what this function returns.
            # So, if it's not a payment, we should aim to return values that reflect "logged out" or previous state if possible.
            # For simplicity, default to logged out unless payment is confirmed.

            out_username = ""
            out_credits = "未登录"
            out_acc_status = "请登录"
            out_payment_res = ""
            out_payment_frame = gr.update(visible=False)  # Hide iframe by default

            # Check for payment redirect parameters (these are examples, adjust to your gateway)
            # Common parameters from payment gateways:
            # out_trade_no: Your order ID
            # trade_no: Payment gateway's transaction ID
            # result/status: Success/failure
            # sign: Signature to verify authenticity
            # amount/money: Paid amount
            # For EasyPay, it's often: trade_status=TRADE_SUCCESS, out_trade_no, trade_no, money, type, sign

            # Let's assume your payment gateway (like a local mock or EasyPay) redirects with these params on success:
            returned_out_trade_no = params.get("out_trade_no")
            returned_trade_no = params.get("trade_no")  # Gateway's transaction ID
            returned_money = params.get("money")  # Amount paid
            returned_trade_status = params.get("trade_status")  # e.g., "TRADE_SUCCESS"

            if returned_out_trade_no and returned_trade_status == "TRADE_SUCCESS":
                logger.info(
                    f"Payment return detected. Order: {returned_out_trade_no}, Status: {returned_trade_status}, Amount: {returned_money}"
                )

                # IMPORTANT: Here you would also verify the `sign` parameter from the payment gateway
                # to ensure the request is legitimate and not tampered with.
                # e.g. if not credits_manager.verify_payment_notification(params):
                #          logger.error("Payment notification signature verification failed!")
                #          return out_username, out_credits, "支付验证失败", "回调签名错误", out_payment_frame

                # Fetch the username associated with this out_trade_no from your DB
                # This is crucial because current_username in Gradio state is lost.
                order_info = credits_manager.db_manager.fetch_one(
                    "SELECT username FROM payment_orders WHERE out_trade_no = ?",
                    (returned_out_trade_no,),
                )
                if order_info and order_info[0]:
                    username_from_order = order_info[0]
                    logger.info(
                        f"Order {returned_out_trade_no} belongs to user {username_from_order}."
                    )

                    # Process the payment (add credits, mark order as complete)
                    # This function needs to be robust and idempotent.
                    processing_result = credits_manager.handle_successful_payment_notification(
                        returned_out_trade_no,
                        returned_trade_no,
                        returned_money,
                        username_from_order,  # Pass username from order to the handler
                    )

                    if processing_result.get("success"):
                        out_username = processing_result["username"]
                        out_credits = f"{processing_result['new_credits']} 点"
                        out_acc_status = "充值成功，已自动登录。"  # Or "点数已更新"
                        out_payment_res = processing_result["message"]

                        # Attempt to "re-login" the user in Gradio's context by setting current_username
                        # The session_id might still be an issue for other operations until a full login,
                        # but at least the username and credits are updated.
                        # A full re-login would require password, which we don't have here.
                        # So, we are setting the username, but session might be invalid.
                        # For operations requiring session_id, user might need to manually login again if issues.
                        logger.info(
                            f"Payment success for {out_username}. Credits: {out_credits}. Result: {out_payment_res}"
                        )

                    else:  # Payment processing failed (e.g., already processed, DB error)
                        out_username = (
                            username_from_order  # Still show username if known
                        )
                        out_credits = f"{credits_manager.get_user_credits(username_from_order)} 点"  # Show current credits
                        out_acc_status = "充值处理遇到问题"
                        out_payment_res = processing_result.get(
                            "message", "支付后处理失败，请联系客服。"
                        )
                        logger.error(
                            f"Payment processing failed for {username_from_order}: {out_payment_res}"
                        )
                else:
                    logger.error(
                        f"Payment return for unknown order: {returned_out_trade_no}"
                    )
                    out_acc_status = "支付处理错误"
                    out_payment_res = "未找到对应订单，请联系客服。"

            # This return tuple updates the Gradio components listed in `outputs` of `demo.load`
            # Order: current_username, user_credits, account_status, payment_result, payment_frame
            return (
                out_username,
                out_credits,
                out_acc_status,
                out_payment_res,
                out_payment_frame,
            )

        # `demo.load` is triggered when the interface is loaded or reloaded.
        # We pass `current_username` (the State object) as an input so its initial value
        # (which will be its default, likely empty string after a full reload) can be accessed if needed,
        # though `handle_page_load_and_payment_redirect` primarily relies on URL params for payment.
        # `gr.Request` is automatically passed if type-hinted in the function.
        demo.load(
            fn=handle_page_load_and_payment_redirect,
            inputs=None,  # No explicit Gradio state inputs needed if gr.Request is sufficient.
            # If you needed to know the state *before* reload, that's not possible.
            # current_username state will be its initial value here.
            outputs=[
                current_username,  # gr.State
                user_credits,  # gr.Textbox
                account_status,  # gr.Textbox
                payment_result,  # gr.Textbox
                payment_frame,  # gr.HTML
            ],
        )

    return demo


# To test the payment redirect locally, you might need a mock payment processor endpoint
# This is OUTSIDE the Gradio app usually, or a special route IF Gradio supports it (it does via FastAPI).
# For simplicity, we'll add it to the underlying FastAPI app Gradio uses.

_payment_app = None

if __name__ == "__main__":
    gradio_app_instance = create_gradio_interface()

    # Add a mock payment processing endpoint to the underlying FastAPI app
    # This simulates the payment gateway's page and its eventual redirect.
    # This is just for local testing convenience.
    import uvicorn
    from fastapi import FastAPI, Request as FastAPIRequest
    from fastapi.responses import HTMLResponse, RedirectResponse

    # Get the FastAPI app from Gradio
    # Note: This method of getting the FastAPI app might change with Gradio versions.
    # For Gradio 4.x, Blocks.launch() returns a server object, but direct app access is cleaner with mount.
    # A more robust way is to create FastAPI app first, then mount Gradio.

    # Let's create FastAPI app and mount Gradio
    fastapi_app = FastAPI()

    # Mock Payment Processor Endpoint (for testing iframe source)
    @fastapi_app.get("/_internal_mock_payment_processor_")
    async def mock_payment_processor(
        request: FastAPIRequest, out_trade_no: str, username: str, amount: float
    ):
        # This page would normally be the payment gateway's UI (QR code etc.)
        # After "payment", it redirects to the Gradio app's main page with parameters.

        # Simulate successful payment redirect URL (like what EasyPay would do)
        # Note: Gradio app runs at root "/"
        # This needs to be the main URL of your Gradio app.
        gradio_app_main_url = str(request.base_url)  # e.g. http://localhost:7890/

        # Parameters that a real payment gateway might send back:
        redirect_url_with_params = (
            f"{gradio_app_main_url}?"
            f"out_trade_no={out_trade_no}&"
            f"trade_no=MOCK_GATEWAY_{out_trade_no[-5:]}&"  # Mock gateway transaction ID
            f"money={amount:.2f}&"
            f"name=充值{amount:.2f}元&"
            f"type=alipay&"  # Example
            f"trade_status=TRADE_SUCCESS&"  # CRUCIAL for success detection
            f"sign=mocksignstring&sign_type=MD5"  # Mock signature
        )

        # This HTML simulates a payment page. User clicks "Pay", then gets redirected.
        html_content = f"""
        <html><head><title>Mock Pay</title></head><body>
            <h2>Mock Payment Page</h2>
            <p>User: {username}</p>
            <p>Order ID: {out_trade_no}</p>
            <p>Amount: {amount:.2f} CNY</p>
            <p>This is a mock payment page. In a real scenario, you'd see a QR code or payment form here.</p>
            <button onclick="finalizePayment()">Simulate Successful Payment</button>
            <script>
                function finalizePayment() {{
                    // Redirect the entire window, not just the iframe
                    window.top.location.href = "{redirect_url_with_params}";
                }}
            </script>
        </body></html>
        """
        return HTMLResponse(content=html_content)

    # Mount Gradio app
    # The path "/gradio" is if you want FastAPI to serve other routes.
    # If Gradio is the main app, mount at "/".
    # gradio_app_instance.favicon_path = "favicon.ico" # Optional: set favicon
    fastapi_app = gr.mount_gradio_app(fastapi_app, gradio_app_instance, path="/")

    logger.info("Starting Gradio app with FastAPI server on http://localhost:7890")
    # Run with Uvicorn
    uvicorn.run(fastapi_app, host="0.0.0.0", port=7890)
    # Old way: demo.launch(share=False, server_name="0.0.0.0", server_port=7890)
